package models;

import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.OneToMany;

import play.db.jpa.Model;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="discriminator", discriminatorType=DiscriminatorType.INTEGER)
public abstract class Place extends Model {
	@Column(nullable=false)
	private String name;
	
	@Column(nullable=false)
	private boolean removed;
	
	@Embedded
	@AttributeOverrides( {
         @AttributeOverride(name="latitude", column = @Column(nullable=true)),
         @AttributeOverride(name="longitude", column = @Column(nullable=true))
	} )
	private Location location;

	@Embedded
	@AttributeOverrides( {
        @AttributeOverride(name="address", column = @Column(nullable=false)),
        @AttributeOverride(name="city", column = @Column(nullable=false)),
        @AttributeOverride(name="state", column = @Column(nullable=false)),
        @AttributeOverride(name="zip", column = @Column(nullable=false)),
        @AttributeOverride(name="country", column = @Column(nullable=false))
	} )
	private Address address;
	
	@OneToMany(orphanRemoval=true, cascade=CascadeType.ALL, fetch=FetchType.LAZY)
	private Set<Telephone> telephones = new HashSet<Telephone>();
	
	@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true, fetch=FetchType.LAZY)
	private Set<Picture> pictures = new HashSet<Picture>();

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Location getLocation() {
		return location;
	}

	public void setLocation(Location location) {
		this.location = location;
	}
	
	public Set<Telephone> getTelephones() {
		return telephones;
	}
	
	public Telephone getTelephone() {
		if (getTelephones() != null && getTelephones().iterator().hasNext()) {
			return getTelephones().iterator().next();
		}
		return null;
	}

	public void setTelephones(Set<Telephone> telephones) {
		this.telephones = telephones;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	public Set<Picture> getPictures() {
		return pictures;
	}
	
	public Picture getPicture() {
		if (getPictures() != null && getPictures().iterator().hasNext()) {
			return getPictures().iterator().next();
		}
		return null;
	}

	public void setPictures(Set<Picture> pictures) {
		this.pictures = pictures;
	}
	
	public boolean isRemoved() {
		return removed;
	}

	public void setRemoved(boolean removed) {
		this.removed = removed;
	}

	protected abstract String getType();
	public abstract <T extends Place> JsonSerializer<Collection<T>> getPlacesSerializer();
	public abstract <T extends Place> JsonSerializer<T> getPlaceSerializer();

	public static class PlacesJsonSerializer<T extends Place> implements JsonSerializer<Collection<T>> {
		@Override
		public JsonElement serialize(Collection<T> places, Type type,
				JsonSerializationContext context) {
			
			JsonArray jsonPlaces = new JsonArray();
			
			for (T place : places) {
				jsonPlaces.add(place.getPlaceSerializer().serialize(place, type, context));
			}
			
			return jsonPlaces;
		}
	}
	
	public static class PlaceJsonSerializer<T extends Place> implements JsonSerializer<T> {
		@Override
		public JsonElement serialize(T place, Type type,
				JsonSerializationContext context) {
			JsonObject jsonPlace =  new JsonObject();
			
			JsonObject address =  new JsonObject();
			address.addProperty("address", place.getAddress().getAddress());
			address.addProperty("city", place.getAddress().getCity());
			address.addProperty("state", place.getAddress().getState());
			address.addProperty("zip", place.getAddress().getZip());
			
			JsonArray telephones =  new JsonArray();
			for (Telephone telephone : place.getTelephones()) {
				JsonObject object = new JsonObject();
				object.addProperty("type", telephone.getType().ordinal());
				object.addProperty("number", telephone.getNumber());
				telephones.add(object);
			}
			
			if (place.getPictures().isEmpty() == false) {
				JsonObject picture = new JsonObject();
				picture.addProperty("URL", place.getPictures().iterator().next().getURL());
				jsonPlace.add("picture", picture);
			}
			jsonPlace.addProperty("id", place.getId());
			jsonPlace.addProperty("name", place.getName());
			jsonPlace.add("address", address);
			jsonPlace.add("telephones", telephones);

			return jsonPlace;
		}
	}
}
